package wancache

import (
	"fmt"
	"io"
	"net/http"
	"net/url"
	"sync"
	"wan-cache/consistent_hash"

	"github.com/sirupsen/logrus"
)

const (
	defaultBasePath = "/wancache"
	defaultReplicas = 50
)

type httpGetter struct {
	baseUrl string
}

func (h *httpGetter) Get(group, key string) ([]byte, error) {
	u := fmt.Sprintf(
		"%v%v/%v",
		h.baseUrl,
		url.QueryEscape(group),
		url.QueryEscape(key),
	)

	resp, err := http.Get(u)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("request returned: %s", resp.Status)
	}

	bodySrc, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("reading response body: %v", err)
	}

	return bodySrc, nil
}

type CacheServer struct {
	addr        string
	basePath    string
	mu          sync.Mutex
	peers       *consistent_hash.Map
	httpGetters map[string]*httpGetter
}

func NewCacheServer(addr string) *CacheServer {
	return &CacheServer{
		addr:     addr,
		basePath: defaultBasePath,
	}
}

func (s *CacheServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	switch req.URL.Path {
	case s.basePath:
		handleBasePath(w, req)
	default:
		handleWrongPath(w, req)
	}
}

func handleBasePath(w http.ResponseWriter, req *http.Request) {
	switch req.Method {
	case "GET":
		handleGetDataFromCache(w, req)
	default:
		logrus.Infof("method: %s is not allowed", req.Method)
		handleWrongMethod(w, req)
	}
}

func handleGetDataFromCache(w http.ResponseWriter, req *http.Request) {
	groupName := req.FormValue("group")
	group := GetGroup(groupName)
	if group == nil {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte("group is not exist"))
		return
	}

	key := req.FormValue("key")
	data, err := group.Get(key)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(err.Error()))
		return
	}

	w.WriteHeader(http.StatusOK)
	w.Write(data.b)
}

func handleWrongMethod(w http.ResponseWriter, req *http.Request) {
	w.WriteHeader(http.StatusMethodNotAllowed)
	w.Write([]byte(req.Method + " is not allowed"))
}

func handleWrongPath(w http.ResponseWriter, req *http.Request) {
	w.WriteHeader(http.StatusNotFound)
	w.Write([]byte("url is invalid, 404"))
}
